"use strict";
(function () {

    //------------------------------
    // Mesh Properties
    //------------------------------
    var MESH = {
        width: 1,
        height: 1,
        depth: 20,
        segments: 4,
        slices: 2,
        xRange: 1,
        yRange: 1,
        zRange: 1,
        ambient: '#bbbbbb',
        diffuse: '#aaaaaa',
        speed: 0.0002
    };
  //------------------------------
  // Light Properties
  //------------------------------
  var LIGHT = {
    count: 2,
    xyScalar: 1,
    zOffset: 100,
    ambient: ambient_value,
    diffuse: diffuse_value,
    speed: 0.001,
    gravity: 1200,
    dampening: 0.95,
    minLimit: 10,
    maxLimit: null,
    minDistance: 20,
    maxDistance: 400,
    autopilot: true,
    draw: false,
    bounds: FSS.Vector3.create(),
    step: FSS.Vector3.create(
      Math.randomInRange(0.2, 1.0),
      Math.randomInRange(0.2, 1.0),
      Math.randomInRange(0.2, 1.0)
    )
  };

    //------------------------------
    // Render Properties
    //------------------------------
    var WEBGL = 'webgl';
    var CANVAS = 'canvas';
    var SVG = 'svg';
    var RENDER = {
        renderer: CANVAS
    };

    //------------------------------
    // Export Properties
    //------------------------------
    var EXPORT = {
        width: 2000,
        height: 1000,
        drawLights: false,
        minLightX: 0.4,
        maxLightX: 0.6,
        minLightY: 0.2,
        maxLightY: 0.4,
        export: function () {
            var l, x, y, light,
          depth = MESH.depth,
          zOffset = LIGHT.zOffset,
          autopilot = LIGHT.autopilot,
          scalar = this.width / renderer.width;

            LIGHT.autopilot = true;
            LIGHT.draw = this.drawLights;
            LIGHT.zOffset *= scalar;
            MESH.depth *= scalar;

            resize(this.width, this.height);

            for (l = scene.lights.length - 1; l >= 0; l--) {
                light = scene.lights[l];
                x = Math.randomInRange(this.width * this.minLightX, this.width * this.maxLightX);
                y = Math.randomInRange(this.height * this.minLightY, this.height * this.maxLightY);
                FSS.Vector3.set(light.position, x, this.height - y, this.lightZ);
                FSS.Vector3.subtract(light.position, center);
            }

            update();
            render();

            switch (RENDER.renderer) {
                case WEBGL:
                    window.open(webglRenderer.element.toDataURL(), '_blank');
                    break;
                case CANVAS:
                    window.open(canvasRenderer.element.toDataURL(), '_blank');
                    break;
                case SVG:
                    var data = encodeURIComponent(output.innerHTML);
                    var url = "data:image/svg+xml," + data;
                    window.open(url, '_blank');
                    break;
            }

            LIGHT.draw = true;
            LIGHT.autopilot = autopilot;
            LIGHT.zOffset = zOffset;
            MESH.depth = depth;

            resize(container.offsetWidth, container.offsetHeight);
        }
    };

    //------------------------------
    // UI Properties
    //------------------------------
    var UI = {
        show: true
    };

    //------------------------------
    // Global Properties
    //------------------------------
    var now, start = Date.now();
    var center = FSS.Vector3.create();
    var attractor = FSS.Vector3.create();
    var container = document.getElementById('container');
    var controls = document.getElementById('controls');
    var output = document.getElementById('output');
    var ui = document.getElementById('ui');
    var renderer, scene, mesh, geometry, material;
    var webglRenderer, canvasRenderer, svgRenderer;
    var gui, autopilotController;

    //------------------------------
    // Methods
    //------------------------------
    function initialise() {
        createRenderer();
        createScene();
        createMesh();
        createLights();
        addEventListeners();
        //addControls();
        resize(container.offsetWidth, container.offsetHeight);
        animate();
    }

    function createRenderer() {
        webglRenderer = new FSS.WebGLRenderer();
        canvasRenderer = new FSS.CanvasRenderer();
        svgRenderer = new FSS.SVGRenderer();
        setRenderer(RENDER.renderer);
    }

    function setRenderer(index) {
        if (renderer) {
            output.removeChild(renderer.element);
        }
        switch (index) {
            case WEBGL:
                renderer = webglRenderer;
                break;
            case CANVAS:
                renderer = canvasRenderer;
                break;
            case SVG:
                renderer = svgRenderer;
                break;
        }
        renderer.setSize(container.offsetWidth, container.offsetHeight);
        output.appendChild(renderer.element);
    }

    function createScene() {
        scene = new FSS.Scene();
    }

    function createMesh() {
        scene.remove(mesh);
        renderer.clear();
        geometry = new FSS.Plane(MESH.width * renderer.width, MESH.height * renderer.height, MESH.segments, MESH.slices);
        material = new FSS.Material(MESH.ambient, MESH.diffuse);
        mesh = new FSS.Mesh(geometry, material);
        scene.add(mesh);

        // Augment vertices for animation
        var v, vertex;
        for (v = geometry.vertices.length - 1; v >= 0; v--) {
            vertex = geometry.vertices[v];
            vertex.anchor = FSS.Vector3.clone(vertex.position);
            vertex.step = FSS.Vector3.create(
        Math.randomInRange(0.2, 1.0),
        Math.randomInRange(0.2, 1.0),
        Math.randomInRange(0.2, 1.0)
      );
            vertex.time = Math.randomInRange(0, Math.PIM2);
        }
    }

    function createLights() {
        var l, light;
        for (l = scene.lights.length - 1; l >= 0; l--) {
            light = scene.lights[l];
            scene.remove(light);
        }
        renderer.clear();
        for (l = 0; l < LIGHT.count; l++) {
            light = new FSS.Light(LIGHT.ambient, LIGHT.diffuse);
            light.ambientHex = light.ambient.format();
            light.diffuseHex = light.diffuse.format();
            scene.add(light);

            // Augment light for animation
            light.mass = Math.randomInRange(0.5, 1);
            light.velocity = FSS.Vector3.create();
            light.acceleration = FSS.Vector3.create();
            light.force = FSS.Vector3.create();

            // Ring SVG Circle
            light.ring = document.createElementNS(FSS.SVGNS, 'circle');
            light.ring.setAttributeNS(null, 'stroke', light.ambientHex);
            light.ring.setAttributeNS(null, 'stroke-width', '0.5');
            light.ring.setAttributeNS(null, 'fill', 'none');
            light.ring.setAttributeNS(null, 'r', '10');

            // Core SVG Circle
            light.core = document.createElementNS(FSS.SVGNS, 'circle');
            light.core.setAttributeNS(null, 'fill', light.diffuseHex);
            light.core.setAttributeNS(null, 'r', '4');
        }
    }

    function resize(width, height) {
        renderer.setSize(width, height);
        FSS.Vector3.set(center, renderer.halfWidth, renderer.halfHeight);
        createMesh();
    }

    function animate() {
        now = Date.now() - start;
        update();
        render();
        requestAnimationFrame(animate);
    }

    function update() {
        var ox, oy, oz, l, light, v, vertex, offset = MESH.depth / 2;

        // Update Bounds
        FSS.Vector3.copy(LIGHT.bounds, center);
        FSS.Vector3.multiplyScalar(LIGHT.bounds, LIGHT.xyScalar);

        // Update Attractor
        FSS.Vector3.setZ(attractor, LIGHT.zOffset);

        // Overwrite the Attractor position
        if (LIGHT.autopilot) {
            ox = Math.sin(LIGHT.step[0] * now * LIGHT.speed);
            oy = Math.cos(LIGHT.step[1] * now * LIGHT.speed);
            FSS.Vector3.set(attractor,
        LIGHT.bounds[0] * ox,
        LIGHT.bounds[1] * oy,
        LIGHT.zOffset);
        }

        // Animate Lights
        for (l = scene.lights.length - 1; l >= 0; l--) {
            light = scene.lights[l];

            // Reset the z position of the light
            FSS.Vector3.setZ(light.position, LIGHT.zOffset);

            // Calculate the force Luke!
            var D = Math.clamp(FSS.Vector3.distanceSquared(light.position, attractor), LIGHT.minDistance, LIGHT.maxDistance);
            var F = LIGHT.gravity * light.mass / D;
            FSS.Vector3.subtractVectors(light.force, attractor, light.position);
            FSS.Vector3.normalise(light.force);
            FSS.Vector3.multiplyScalar(light.force, F);

            // Update the light position
            FSS.Vector3.set(light.acceleration);
            FSS.Vector3.add(light.acceleration, light.force);
            FSS.Vector3.add(light.velocity, light.acceleration);
            FSS.Vector3.multiplyScalar(light.velocity, LIGHT.dampening);
            FSS.Vector3.limit(light.velocity, LIGHT.minLimit, LIGHT.maxLimit);
            FSS.Vector3.add(light.position, light.velocity);
        }

        // Animate Vertices
        for (v = geometry.vertices.length - 1; v >= 0; v--) {
            vertex = geometry.vertices[v];
            ox = Math.sin(vertex.time + vertex.step[0] * now * MESH.speed);
            oy = Math.cos(vertex.time + vertex.step[1] * now * MESH.speed);
            oz = Math.sin(vertex.time + vertex.step[2] * now * MESH.speed);
            FSS.Vector3.set(vertex.position,
        MESH.xRange * geometry.segmentWidth * ox,
        MESH.yRange * geometry.sliceHeight * oy,
        MESH.zRange * offset * oz - offset);
            FSS.Vector3.add(vertex.position, vertex.anchor);
        }

        // Set the Geometry to dirty
        geometry.dirty = true;
    }

    function render() {
        renderer.render(scene);

        // Draw Lights
        if (LIGHT.draw) {
            var l, lx, ly, light;
            for (l = scene.lights.length - 1; l >= 0; l--) {
                light = scene.lights[l];
                lx = light.position[0];
                ly = light.position[1];
                switch (RENDER.renderer) {
                    case CANVAS:
                        renderer.context.lineWidth = 0.5;
                        renderer.context.beginPath();
                        renderer.context.arc(lx, ly, 10, 0, Math.PIM2);
                        renderer.context.strokeStyle = light.ambientHex;
                        renderer.context.stroke();
                        renderer.context.beginPath();
                        renderer.context.arc(lx, ly, 4, 0, Math.PIM2);
                        renderer.context.fillStyle = light.diffuseHex;
                        renderer.context.fill();
                        break;
                    case SVG:
                        lx += renderer.halfWidth;
                        ly = renderer.halfHeight - ly;
                        light.core.setAttributeNS(null, 'fill', light.diffuseHex);
                        light.core.setAttributeNS(null, 'cx', lx);
                        light.core.setAttributeNS(null, 'cy', ly);
                        renderer.element.appendChild(light.core);
                        light.ring.setAttributeNS(null, 'stroke', light.ambientHex);
                        light.ring.setAttributeNS(null, 'cx', lx);
                        light.ring.setAttributeNS(null, 'cy', ly);
                        renderer.element.appendChild(light.ring);
                        break;
                }
            }
        }
    }

    function addEventListeners() {
        window.addEventListener('resize', onWindowResize);
        container.addEventListener('click', onMouseClick);
        container.addEventListener('mousemove', onMouseMove);
    }

    function addControls() {
        var i, l, light, folder, controller;

        // Create GUI
        gui = new dat.GUI({ autoPlace: false });
        controls.appendChild(gui.domElement);

        // Create folders
        uiFolder = gui.addFolder('UI');
        renderFolder = gui.addFolder('Render');
        meshFolder = gui.addFolder('Mesh');
        lightFolder = gui.addFolder('Light');
        exportFolder = gui.addFolder('Export');

        // Open folders
        uiFolder.open();
        renderFolder.open();
        // meshFolder.open();
        lightFolder.open();
        // exportFolder.open();

        // Add UI Controls
        controller = uiFolder.add(UI, 'show');
        controller.onChange(function (value) {
            ui.className = value ? 'wrapper' : 'wrapper hide';
        });

        // Add Render Controls
        controller = renderFolder.add(RENDER, 'renderer', { webgl: WEBGL, canvas: CANVAS, svg: SVG });
        controller.onChange(function (value) {
            setRenderer(value);
        });

        // Add Mesh Controls
        controller = meshFolder.addColor(MESH, 'ambient');
        controller.onChange(function (value) {
            for (i = 0, l = scene.meshes.length; i < l; i++) {
                scene.meshes[i].material.ambient.set(value);
            }
        });
        controller = meshFolder.addColor(MESH, 'diffuse');
        controller.onChange(function (value) {
            for (i = 0, l = scene.meshes.length; i < l; i++) {
                scene.meshes[i].material.diffuse.set(value);
            }
        });
        controller = meshFolder.add(MESH, 'width', 0.05, 2);
        controller.onChange(function (value) {
            if (geometry.width !== value * renderer.width) { createMesh(); }
        });
        controller = meshFolder.add(MESH, 'height', 0.05, 2);
        controller.onChange(function (value) {
            if (geometry.height !== value * renderer.height) { createMesh(); }
        });
        controller = meshFolder.add(MESH, 'depth', 0, 50);
        controller = meshFolder.add(MESH, 'segments', 1, 20);
        controller.step(1);
        controller.onChange(function (value) {
            if (geometry.segments !== value) { createMesh(); }
        });
        controller = meshFolder.add(MESH, 'slices', 1, 20);
        controller.step(1);
        controller.onChange(function (value) {
            if (geometry.slices !== value) { createMesh(); }
        });
        controller = meshFolder.add(MESH, 'xRange', 0, 1);
        controller = meshFolder.add(MESH, 'yRange', 0, 1);
        controller = meshFolder.add(MESH, 'speed', 0, 0.01);

        // Add Light Controls
        autopilotController = lightFolder.add(LIGHT, 'autopilot');
        controller = lightFolder.addColor(LIGHT, 'ambient');
        controller.onChange(function (value) {
            for (i = 0, l = scene.lights.length; i < l; i++) {
                light = scene.lights[i];
                light.ambient.set(value);
                light.ambientHex = light.ambient.format();
            }
        });
        controller = lightFolder.addColor(LIGHT, 'diffuse');
        controller.onChange(function (value) {
            for (i = 0, l = scene.lights.length; i < l; i++) {
                light = scene.lights[i];
                light.diffuse.set(value);
                light.diffuseHex = light.diffuse.format();
            }
        });
        controller = lightFolder.add(LIGHT, 'count', 0, 5);
        controller.step(1);
        controller.onChange(function (value) {
            if (scene.lights.length !== value) { createLights(); }
        });
        controller = lightFolder.add(LIGHT, 'zOffset', 0, 500);
        controller.step(1);

        // Add Export Controls
        controller = exportFolder.add(EXPORT, 'width', 100, 4000);
        controller.step(100);
        controller = exportFolder.add(EXPORT, 'height', 100, 4000);
        controller.step(100);
        controller = exportFolder.add(EXPORT, 'drawLights');
        controller = exportFolder.add(EXPORT, 'minLightX', 0, 1);
        controller = exportFolder.add(EXPORT, 'maxLightX', 0, 1);
        controller = exportFolder.add(EXPORT, 'minLightY', 0, 1);
        controller = exportFolder.add(EXPORT, 'maxLightY', 0, 1);
        controller = exportFolder.add(EXPORT, 'export');
    }

    //------------------------------
    // Callbacks
    //------------------------------
    function onMouseClick(event) {
        FSS.Vector3.set(attractor, event.x, renderer.height - event.y);
        FSS.Vector3.subtract(attractor, center);
        LIGHT.autopilot = !LIGHT.autopilot;
        //autopilotController.updateDisplay();
    }

    function onMouseMove(event) {
        FSS.Vector3.set(attractor, event.x, renderer.height - event.y);
        FSS.Vector3.subtract(attractor, center);
    }

    function onWindowResize(event) {
        resize(container.offsetWidth, container.offsetHeight);
        render();
    }



    // Let there be light!
    initialise();

})();